﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Management;
using System.Text.RegularExpressions;

namespace PsArgs
{
    static class Program
    {
        private static readonly Regex ExeRegex = new Regex(@"^(?(?=^"")(?<exe>^""[^""]*"")|(?<exe>[^\s]*))", RegexOptions.Compiled | RegexOptions.CultureInvariant);

        private static void WriteLine(object name, string serviceName, object processId, object caption, object path, object args)
        {
            WriteLine(SafeToString(name), serviceName, SafeToString(processId), SafeToString(caption), SafeToString(path), GetArgumentsFromCommandLine(args));
        }

        private static string GetArgumentsFromCommandLine(object args)
        {
            var argumentsFromCommandLine = SafeToString(args);

            if (string.IsNullOrEmpty(argumentsFromCommandLine))
            {
                return argumentsFromCommandLine;
            }

            var exeFound = ExeRegex.Matches(argumentsFromCommandLine).Cast<Match>().First().Groups["exe"].Value;
            var parsedArgs = argumentsFromCommandLine.Replace(exeFound, string.Empty).Trim();
            return parsedArgs;
        }

        private static string SafeToString(object o)
        {
            return o != null ? o.ToString() : string.Empty;
        }

        private static void WriteLine(string name, string serviceName, string processId, string caption, string path, string args)
        {
            var strings = new[] { Path.GetFileNameWithoutExtension(name), serviceName, processId, caption, path, args };

            var outMessage = string.Join("\t", strings);

            Console.WriteLine(outMessage);
        }

        private static void WriteHeader()
        {
            WriteLine("name", "service", "pid", "caption", "path", "args");
        }

        static void Main()
        {
            WriteHeader();

            const string name = "Name";
            const string processid = "ProcessId";
            const string caption = "Caption";
            const string executablepath = "ExecutablePath";
            const string commandline = "CommandLine";

            var wmiQuery = new SelectQuery("Win32_Process", "", new[] { name, processid, caption, executablepath, commandline });

            var scope = new ManagementScope(@"\\.\root\CIMV2");
            var searcher = new ManagementObjectSearcher(scope, wmiQuery);
            var win32Process = searcher.Get();

            var win32Services = new ManagementObjectSearcher(scope, new SelectQuery("Win32_Service", "", new[] { processid, name })).Get();

            var services = win32Services
                .Cast<ManagementBaseObject>()
                .Select(x => new KeyValuePair<int, string>(int.Parse(x[processid].ToString()), x[name].ToString()))
                .Where(x => x.Key != 0)
                .ToList();

            foreach (var obj in win32Process)
            {
                var pid = int.Parse(obj[processid].ToString());
                WriteLine(obj[name], GetServiceName(services, pid), pid, obj[caption], obj[executablepath], obj[commandline]);
            }
        }

        private static string GetServiceName(IEnumerable<KeyValuePair<int, string>> services, int pid)
        {
            var candidates = services.Where(x => x.Key == pid).ToList();
            return (candidates.Any()) ? candidates.First().Value : string.Empty;
        }
    }
}
